-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
feat: implement BalanceEmptyState component #21391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
1403362 to
874da16
Compare
874da16 to
88f746b
Compare
51cc193 to
33d1845
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #21391 +/- ##
==========================================
+ Coverage 76.98% 77.09% +0.11%
==========================================
Files 3715 3719 +4
Lines 93270 93446 +176
Branches 17894 17929 +35
==========================================
+ Hits 71801 72046 +245
+ Misses 16580 16508 -72
- Partials 4889 4892 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
f1ac442 to
175af1f
Compare
app/components/UI/Assets/components/Balance/AccountGroupBalance.test.tsx
Show resolved
Hide resolved
8080bee to
b36ea70
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements the BalanceEmptyState component on the homepage when users have a zero balance across all networks, replacing the default zero balance display with an engaging card featuring a "Fund your wallet" illustration and "Add funds" button to improve the onboarding experience for new users.
Key changes:
- Integrated
BalanceEmptyStateinto bothPortfolioBalance(legacy multichain) andAccountGroupBalance(multichain accounts state 2) components - Replaced
ButtonHerowith standardButtoncomponent inBalanceEmptyState - Removed
TokenListFooterfrom token list display
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| app/components/UI/Tokens/TokenList/index.tsx | Removed TokenListFooter component from list display |
| app/components/UI/Tokens/TokenList/PortfolioBalance/index.tsx | Integrated BalanceEmptyState for zero balance display |
| app/components/UI/Tokens/TokenList/PortfolioBalance/index.test.tsx | Added comprehensive test coverage for empty state scenarios |
| app/components/UI/BalanceEmptyState/BalanceEmptyState.tsx | Replaced ButtonHero with standard Button component |
| app/components/UI/Assets/components/Balance/AccountGroupBalance.tsx | Integrated BalanceEmptyState for zero balance display |
| app/components/UI/Assets/components/Balance/AccountGroupBalance.test.tsx | Added test coverage for empty state scenarios |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
app/components/UI/Tokens/TokenList/PortfolioBalance/index.test.tsx
Outdated
Show resolved
Hide resolved
app/components/UI/Assets/components/Balance/AccountGroupBalance.test.tsx
Outdated
Show resolved
Hide resolved
app/components/hooks/useMultichainBalances/useMultichainBalances.types.ts
Show resolved
Hide resolved
| const isHomepageRedesignV1Enabled = useSelector( | ||
| selectHomepageRedesignV1Enabled, | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using new feature flag to conditionally render the balance empty state
172814e to
d6a61a8
Compare
4115e16 to
2078f33
Compare
| // This selector is used to display the BalanceEmptyState | ||
| selectAccountGroupBalanceForEmptyState: jest.fn(() => null), | ||
| })); | ||
|
|
||
| // Mock homepage redesign feature flag for BalanceEmptyState | ||
| jest.mock('../../../../../selectors/featureFlagController/homepage', () => ({ | ||
| selectHomepageRedesignV1Enabled: jest.fn(() => true), | ||
| })); | ||
|
|
||
| // This selector is used to determine if the current network is a testnet for BalanceEmptyState display logic | ||
| jest.mock('../../../../../selectors/networkController', () => ({ | ||
| ...jest.requireActual('../../../../../selectors/networkController'), | ||
| selectEvmChainId: jest.fn(() => '0x1'), // Ethereum mainnet (not a testnet) | ||
| selectChainId: jest.fn(() => '0x1'), // BalanceEmptyState also needs this | ||
| })); | ||
|
|
||
| // Mock navigation hooks used by BalanceEmptyState | ||
| jest.mock('@react-navigation/native', () => ({ | ||
| ...jest.requireActual('@react-navigation/native'), | ||
| useNavigation: () => ({ | ||
| navigate: jest.fn(), | ||
| goBack: jest.fn(), | ||
| reset: jest.fn(), | ||
| }), | ||
| })); | ||
|
|
||
| // Mock metrics hook used by BalanceEmptyState | ||
| jest.mock('../../../../../components/hooks/useMetrics', () => ({ | ||
| useMetrics: () => ({ | ||
| trackEvent: jest.fn(), | ||
| createEventBuilder: jest.fn(() => ({ record: jest.fn() })), | ||
| }), | ||
| })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding necessary mocks for BalanceEmptyState component
| it('renders empty state when account group balance is zero', () => { | ||
| const { | ||
| selectAccountGroupBalanceForEmptyState, | ||
| selectBalanceBySelectedAccountGroup, | ||
| } = jest.requireMock('../../../../../selectors/assets/balances'); | ||
|
|
||
| // Mock the regular balance selector to return data (prevents skeleton loader) | ||
| (selectBalanceBySelectedAccountGroup as jest.Mock).mockImplementation( | ||
| () => ({ | ||
| totalBalanceInUserCurrency: 100, // Some non-zero amount for current network | ||
| userCurrency: 'usd', | ||
| }), | ||
| ); | ||
|
|
||
| // Mock the empty state selector to return zero balance across all mainnet networks | ||
| (selectAccountGroupBalanceForEmptyState as jest.Mock).mockImplementation( | ||
| () => ({ | ||
| totalBalanceInUserCurrency: 0, // Zero across all mainnet networks | ||
| userCurrency: 'usd', | ||
| }), | ||
| ); | ||
|
|
||
| const { getByTestId } = renderWithProvider(<AccountGroupBalance />, { | ||
| state: testState, | ||
| }); | ||
|
|
||
| const el = getByTestId( | ||
| WalletViewSelectorsIDs.BALANCE_EMPTY_STATE_CONTAINER, | ||
| ); | ||
| expect(el).toBeOnTheScreen(); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding test to check for balance emtpy state
| selectAccountGroupBalanceForEmptyState, | ||
| } from '../../../../../selectors/assets/balances'; | ||
| import { selectHomepageRedesignV1Enabled } from '../../../../../selectors/featureFlagController/homepage'; | ||
| import { selectEvmChainId } from '../../../../../selectors/networkController'; | ||
| import { TEST_NETWORK_IDS } from '../../../../../constants/network'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Importing new selector to calculate account group balance across all main nets, the feature flag for the new homepage redesign which includes full page scroll, the selector for current evm network and test networks ids for empty state display logic
| import { Skeleton } from '../../../../../component-library/components/Skeleton'; | ||
| import { useFormatters } from '../../../../hooks/useFormatters'; | ||
| import AccountGroupBalanceChange from '../../components/BalanceChange/AccountGroupBalanceChange'; | ||
| import BalanceEmptyState from '../../../BalanceEmptyState'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Importing BalanceEmptyState ui component
| const accountGroupBalance = useSelector( | ||
| selectAccountGroupBalanceForEmptyState, | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting account group balance across all chains using new selector
| // Check if account group balance (across all mainnet networks) is zero for empty state | ||
| const hasZeroAccountGroupBalance = | ||
| accountGroupBalance && accountGroupBalance.totalBalanceInUserCurrency === 0; | ||
|
|
||
| // Check if current network is a testnet | ||
| const isCurrentNetworkTestnet = TEST_NETWORK_IDS.includes(selectedChainId); | ||
|
|
||
| // Show empty state on accounts with an aggregated mainnet balance of zero | ||
| const shouldShowEmptyState = | ||
| hasZeroAccountGroupBalance && | ||
| isHomepageRedesignV1Enabled && | ||
| !isCurrentNetworkTestnet; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic for displaying Balance empty state.
- Show if the account group has a zero balance across all main nets and
isHomepageRedesignV1Enabledfeature flag is enabled - Do not show if it's a test network regardless of balance - show $0.00
| }); | ||
| }); | ||
|
|
||
| describe('selectAccountGroupBalanceForEmptyState', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding tests for new selector
| import { NON_EVM_TESTNET_IDS } from '@metamask/multichain-network-controller'; | ||
| import { | ||
| parseCaipChainId, | ||
| CaipChainId, | ||
| KnownCaipNamespace, | ||
| } from '@metamask/utils'; | ||
| import { toHex } from '@metamask/controller-utils'; | ||
| import { TEST_NETWORK_IDS } from '../../constants/network'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import helpful consts and utils for new selectAccountGroupBalanceForEmptyState selector
|
| accountGroupBalance && accountGroupBalance.totalBalanceInUserCurrency === 0; | ||
|
|
||
| // Check if current network is a testnet | ||
| const isCurrentNetworkTestnet = TEST_NETWORK_IDS.includes(selectedChainId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently only show EVM test networks but this is a good point. If we were to add solana test net we may see the empty state. Do we have a non EVM test network constant that we can use here. Do you know @salimtb?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
cortisiko
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! 🌮 🌮



Description
This PR implements the
BalanceEmptyStatecomponent on the homepage when users have a zero balance across all mainnet networks. It is hidden behind thehomepageRedesignV1Keyfeature flag which will be used for full page scroll of the homepage.BalanceEmptyStatecomponent for accounts with an aggregated balance of zero across mainnets(excludes testnets)AccountGroupBalancecomponent.TokenListFooter(Removed in this PR: chore: Remove TokenListFooter component in favor of balance empty state #21490)homepageRedesignV1Keyfeature flagselectWalletBalanceForEmptyStateto get balance across all networksChangelog
CHANGELOG entry: Updating zero balance display to "Add funds" card
Related issues
Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-153
Manual testing steps
Screenshots/Recordings
Before
Empty balance showed a
0balance and footer CTA to go to depositbefore720.mov
Loading balance on first import of an account with zero balance
loadingbefore720.mov
After
Empty balance now shows BalanceEmptyState component
after720.mov
The flicker between a zero balance(now empty state) still exists and is an architectural issue outside of the scope of this PR. This is being worked on by the @MetaMask/metamask-assets team
loadingafter720.mov
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Show BalanceEmptyState in
AccountGroupBalancewhen aggregated mainnet balance is zero (feature-flagged), with new selector and comprehensive tests.BalanceEmptyStateinAccountGroupBalancewhen aggregated mainnet balance is zero; hide on testnets; keep skeleton while loading; gated byselectHomepageRedesignV1Enabled.TEST_NETWORK_IDSandselectEvmChainIdto suppress empty state on test networks.selectAccountGroupBalanceForEmptyStateto compute selected group balance across mainnet networks only, using CAIP utilities and excludingTEST_NETWORK_IDSandNON_EVM_TESTNET_IDS.AccountGroupBalance.test.tsxto cover empty state rendering; add selector tests for network filtering, no selection, and missing group fallbacks.balance-empty-state.spec.tsvalidating visibility on mainnet vs testnets, navigation to buy flow, and persistence after restart; extend page objectWalletViewand selectors withBALANCE_EMPTY_STATE_*IDs.Written by Cursor Bugbot for commit 2078f33. This will update automatically on new commits. Configure here.